#include "bc_weir_base.hpp"
#include "bc_weir_funcs.hpp"
#include "field.hpp"
#include "boundaryPatch.hpp"
#include "exceptions.hpp"
#include "formatHelpers.hpp"
#include "unifiedMathFunctions.hpp"
#include "geometry.hpp"

namespace hms {

#define CHEAD(RETURN_TYPE) \
template<typename MeshType> \
RETURN_TYPE Weir_base<MeshType>::

CHEAD() Weir_base(
	Type bcType,
	Field<MeshType>& f,
	const BoundaryPatch& p,
	const Field<MeshType>& d,
	const Field<MeshType>& v,
	scalar slopeParam
) :
	BaseType( bcType, f, p ),
	m_transformer { v, p },
	m_dEdgeBuf  { 1, p.size() }
{
	dField(d);
	vField(v);
	alpha(slopeParam);
}


CHEAD(void) alpha(scalar alphaArg){
	if (alphaArg < 0 || alphaArg > math::pi/2){
		throw InputError( fmt::format(
			"Slope angle alpha={} is not between 0 and pi/2!",
			bold(red("{}", alphaArg))
		) );
	}
	m_alpha = alphaArg;
	cp( bc_weir::pressureFactor(alphaArg) );
}

CHEAD(auto) alpha() const -> scalar {
	assert(m_alpha >= 0 && m_alpha <= math::pi/2 );
	return m_alpha;
}

CHEAD(void) cp(scalar cpArg){
	if (cpArg < 0.1 || cpArg > 1){
		throw InputError( fmt::format(
			"Pressure factor Cp={} is not between 0.1 and 1.0!",
			bold(red("{}", cpArg))
		) );
	}
	m_cp = cpArg;
}

CHEAD(auto) cp() const -> scalar {
	assert(m_cp >= 0.1 && m_cp <= 1);
	return m_cp;
}

CHEAD(auto) dEdge() const -> const Array1Xs& {
	assert( m_dEdgeBuf.cols() == patch().size() );
	return m_dEdgeBuf;
}


CHEAD(void) computeDEdge() const {
	assert( m_dEdgeBuf.cols() == patch().size() );

	const BoundaryPatch& patch { this->patch() };
	const auto& dDomain { dField().domainValues(patch) };

	bc_weir::waterDepth_edge(
		m_dEdgeBuf,
		dDomain,
		m_transformer.transformed().row(0),
		cp()
	);
}

CHEAD(void) updateBuffers(bool orthoOnly) const {
	m_transformer.transform(false, orthoOnly);
	computeDEdge();
}

#undef CHEAD

#define INSTANTIATE_FOR_ALL_MESHTYPES(MeshType) \
template class Weir_base<MeshType>;

#include "meshInstantiator.hpp"

} // namespace hms
